package org.grow.swimming.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.grow.swimming.dao.ShoppingBillRepo;
import org.grow.swimming.dto.*;
import org.grow.swimming.entity.ShoppingBill;
import org.grow.swimming.entity.ShoppingState;
import org.grow.swimming.exception.NoticeException;
import org.grow.swimming.exception.StateException;
import org.grow.swimming.service.CodeGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Author: xwg
 * @CreateDate: 2022/5/7
 */

@RequestMapping("/swimming/shopping")
@RestController
public class ShoppingController {

    @Autowired
    private ShoppingBillRepo shoppingBillRepo;
    @Autowired
    private CodeGenerator codeGenerator;

    @GetMapping("/shoppingStates")
    public List<EnumDto> shoppingStates() {
        return Arrays.stream(ShoppingState.values())
                .map(x -> new EnumDto(
                        x.ordinal(),
                        x.name(),
                        x.getLabel()))
                .collect(Collectors.toList());
    }

    @PostMapping("/insert")
    public ShoppingBill insert(@RequestBody ShoppingBill shoppingBill) {

//        设置空白表单属性
        shoppingBill.setShoppingBillCode(codeGenerator.nextCode("SH"));
        shoppingBill.setAbandon(false);


//       设置内容属性
        shoppingBill.setSupplyCompany(shoppingBill.getSupplyCompany());
        shoppingBill.setSubmitTime(null);
        shoppingBill.refreshComputed();
        shoppingBill.refreshRelationShip();
//        设置流程属性
        shoppingBill.setShoppingState(ShoppingState.toSubmit);
        shoppingBill.setSubmitter(SecurityContextHolder.getContext().getAuthentication().getName());
        shoppingBill.setResubmitTimes(0);
        if (StringUtils.hasText(shoppingBill.getAuditor())) {
            shoppingBill.setAuditor(shoppingBill.getAuditor());
        } else {
            shoppingBill.setAuditor(shoppingBill.getSubmitter());
        }
        shoppingBill.setAuditScore(-1);
//保存
        shoppingBill.setUpdateTime(LocalDateTime.now());
        ShoppingBill save = shoppingBillRepo.save(shoppingBill);
        return save;

    }

    @Autowired
    private ObjectMapper objectMapper;

    @PutMapping("/modify")
    public ShoppingBill modify(@RequestBody ShoppingBill shoppingBill) {
        String shoppingBillCode = shoppingBill.getShoppingBillCode();
        ShoppingBill storedBill = shoppingBillRepo.getById(shoppingBillCode);
        if (null == storedBill) {
            throw new NoticeException("待修改的表单不存在");
        }
        if (storedBill.getAbandon()) {
            throw new NoticeException("待修改的表单已废弃");
        }
        if (storedBill.getShoppingState() != ShoppingState.toSubmit) {
            throw new StateException("只有处于待提交状态的表单才能修改");
        }
        shoppingBill.setSubmitTime(null);
        shoppingBill.refreshComputed();
        shoppingBill.refreshRelationShip();

        //        设置流程属性
        shoppingBill.setShoppingState(ShoppingState.toSubmit);
        shoppingBill.setSubmitter(SecurityContextHolder.getContext().getAuthentication().getName());
        shoppingBill.setResubmitTimes(0);
        if (StringUtils.hasText(shoppingBill.getAuditor())) {
            shoppingBill.setAuditor(shoppingBill.getAuditor());
        } else {
            shoppingBill.setAuditor(shoppingBill.getSubmitter());
        }
        shoppingBill.setAuditScore(-1);

        shoppingBill.setUpdateTime(LocalDateTime.now());
        System.out.println("shoppingBill============");
        System.out.println(shoppingBill);
        try {
            System.out.println(objectMapper.writeValueAsString(shoppingBill));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        ShoppingBill save = shoppingBillRepo.save(shoppingBill);
        System.out.println("save============");
        System.out.println(save);
        try {
            System.out.println(objectMapper.writeValueAsString(save));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return save;
    }

    @PutMapping("/submit/{shoppingBillCode}")
    public String submit(@PathVariable String shoppingBillCode) {
        ShoppingBill storedBill = shoppingBillRepo.getById(shoppingBillCode);
        if (storedBill == null) {
            throw new NoticeException("待提交的表单不存在");
        }
        if (storedBill.getAbandon()) {
            throw new NoticeException("待提交的表单已废弃");
        }
        if (storedBill.getShoppingState() != ShoppingState.toSubmit) {
            throw new StateException("只有处于待提交状态的表单才能提交");
        }
        storedBill.setShoppingState(ShoppingState.toAudit);
        storedBill.setSubmitTime(LocalDateTime.now());

        storedBill.setUpdateTime(LocalDateTime.now());
        shoppingBillRepo.save(storedBill);
        return storedBill.getSubmitTime().toString();
    }

    @PutMapping("/audit")
    public String audit(
            @RequestBody ShoppingAuditDto shoppingAuditDto) {
        String shoppingBillCode = shoppingAuditDto.getShoppingBillCode();
        ShoppingBill storedBill = shoppingBillRepo.getById(shoppingBillCode);
        if (storedBill == null) {
            throw new NoticeException("待提交的表单不存在");
        }
        if (storedBill.getAbandon()) {
            throw new NoticeException("待提交的表单已废弃");
        }
        if (storedBill.getShoppingState() != ShoppingState.toAudit) {
            throw new StateException("只有处于待审核状态的表单才能审核");
        }
        if (shoppingAuditDto.getAuditScore() >= 60) {
            storedBill.setShoppingState(ShoppingState.agreed);
        } else {
            storedBill.setShoppingState(ShoppingState.refused);
        }
        storedBill.setAuditScore(shoppingAuditDto.getAuditScore());
        storedBill.setAuditor(SecurityContextHolder.getContext().getAuthentication().getName());

        storedBill.setUpdateTime(LocalDateTime.now());
        ShoppingBill save = shoppingBillRepo.save(storedBill);
        return save.getShoppingState().name();

    }

    @PutMapping("/resubmit/{shoppingBillCode}")
    public String resubmit(@PathVariable String shoppingBillCode) {
        ShoppingBill storedBill = shoppingBillRepo.getById(shoppingBillCode);
        if (storedBill == null) {
            throw new NoticeException("待提交的表单不存在");
        }
        if (storedBill.getAbandon()) {
            throw new NoticeException("待提交的表单已废弃");
        }
        if (storedBill.getShoppingState() != ShoppingState.toAudit) {
            throw new StateException("只有处于待审核状态的表单才能重交");
        }
        storedBill.setShoppingState(ShoppingState.toSubmit);
        storedBill.setSubmitTime(null);
        storedBill.setResubmitTimes(storedBill.getResubmitTimes() + 1);

        storedBill.setUpdateTime(LocalDateTime.now());
        shoppingBillRepo.save(storedBill);
        return storedBill.getResubmitTimes() + "";
    }

    @PostMapping("/query4submitter")
    public PagedResults<ShoppingBill> query4submitter(@RequestBody SubmitterShoppingQC qc) {
        PageRequest page = PageRequest.of(qc.getPageNum() - 1, qc.getPageSize(),
                Sort.by(Sort.Direction.DESC, "updateTime"));

        Specification<ShoppingBill> s = ((root, query, criteriaBuilder) -> {
            Predicate conjunction = criteriaBuilder.conjunction();
            List<Expression<Boolean>> expressions = conjunction.getExpressions();
            if (qc.getShoppingState() != null) {
                expressions.add(criteriaBuilder.equal(root.get("shoppingState"), qc.getShoppingState()));
            }
            ;
            if (qc.getAmountMin() != null && qc.getAmountMin().intValue() != 0) {
                expressions.add(criteriaBuilder.greaterThanOrEqualTo(root.get("amount"), qc.getAmountMin()));
            }
            if (qc.getAmountMax() != null && qc.getAmountMax().intValue() != 0) {
                expressions.add(criteriaBuilder.lessThanOrEqualTo(root.get("amount"), qc.getAmountMax()));
            }
            expressions.add(criteriaBuilder.isFalse(root.get("abandon")));
            String name = SecurityContextHolder.getContext().getAuthentication().getName();
            expressions.add(criteriaBuilder.equal(root.get("submitter"), name));
            return conjunction;
        });
        Page<ShoppingBill> all = shoppingBillRepo.findAll(s, page);
        return new PagedResults<>(all.getTotalElements(), all.getContent());

    }

    /**
     * private String shoppingBillCode;
     *
     * @OneToMany(mappedBy = "shoppingBill", cascade = {CascadeType.ALL}, orphanRemoval = true)
     * private List<ShoppingBillItem> shoppingBillItems = new ArrayList<>();
     * private BigDecimal amount = new BigDecimal("0");
     * // 业务无关的性能字段 例如逻辑删除乐观锁 创建时间更新时间 1+2
     * private LocalDateTime updateTime;
     * private Boolean abandon;
     * // 业务相关字面量可见字段  3+n
     * private String supplyCompany;
     * private String submitter;
     * private LocalDateTime submitTime;
     * // 业务流程状态字段  1+2
     * @Enumerated(EnumType.STRING) private ShoppingState shoppingState;
     * private String auditor;
     * private Integer auditScore = -1;
     * private Integer resubmitTimes = -1;
     */

    @PostMapping("/query4auditor")
    public PagedResults<ShoppingBill> query4auditor(@RequestBody AuditorShoppingQC qc) {
        PageRequest page = PageRequest.of(qc.getPageNum() - 1, qc.getPageSize(),
                Sort.by(Sort.Direction.DESC, "updateTime"));
        Specification<ShoppingBill> s = ((root, query, criteriaBuilder) -> {
            Predicate conjunction = criteriaBuilder.conjunction();
            List<Expression<Boolean>> expressions = conjunction.getExpressions();
            if (qc.getShoppingState() != null) {
                expressions.add(criteriaBuilder.equal(root.get("shoppingState"), qc.getShoppingState()));
            }
            ;
            expressions.add(criteriaBuilder.isFalse(root.get("abandon")));
            expressions.add(criteriaBuilder.notEqual(root.get("shoppingState"), ShoppingState.toSubmit));
            String name = SecurityContextHolder.getContext().getAuthentication().getName();
            expressions.add(criteriaBuilder.equal(root.get("auditor"), name));
            return conjunction;
        });
        Page<ShoppingBill> all = shoppingBillRepo.findAll(s, page);
        return new PagedResults<ShoppingBill>(all.getTotalElements(), all.getContent());
    }

    @GetMapping("/find/{code}")
    public Optional<ShoppingBill> findByCode(@PathVariable String code) {
        Optional<ShoppingBill> byId = shoppingBillRepo.findById(code);
        return byId;
    }

    @DeleteMapping("/abandon/{code}")
    public Boolean abandonByCode(@PathVariable String code) {
        Optional<ShoppingBill> byId = shoppingBillRepo.findById(code);
        AtomicReference<Boolean> result = new AtomicReference<>(false);
        byId.ifPresent(bill -> {
            bill.setAbandon(true);
            shoppingBillRepo.save(bill);
            result.set(true);
        });
        return result.get();
    }

    // tongji zuijin sangeyue tijiao de tongguode zong jine
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @PutMapping("/ss/echart")
    public List<Map<String, Object>> ssEchart() {
        LocalDate m0 = LocalDate.now();
        LocalDate m1 = m0.minusMonths(1);
        LocalDate m2 = m0.minusMonths(2);
        LocalDate m3 = m0.minusMonths(3);
        List<LocalDate> ms = Arrays.asList(m0, m1, m2, m3);
        String sql = "select count(1) as counted, avg(sb.amount) as avged, sum(sb.amount) as sumed\n" +
                "from ShoppingBill sb\n" +
                "where sb.shoppingState = 'agreed'\n" +
                "  and sb.abandon = false\n" +
                "  and sb.submitTime > '%s'\n" +
                "  and sb.submitTime < '%s'";

        System.out.println(String.format(sql, m1, m0));
        List<Map<String, Object>> results = new LinkedList<>();

        for (int i = 0; i < ms.size() - 1; i++) {
            Map<String, Object> re = jdbcTemplate
                    .queryForMap(String
                            .format(sql, ms.get(i + 1), ms.get(i)));
            re.put("monthly", -(i + 1));
            results.add(re);
        }
//        Map<String, Object> re1 = jdbcTemplate.queryForMap(String.format(sql, m1, m0));
//        re1.put("monthly", -1);
//        results.add(re1);
//        Map<String, Object> re2 = jdbcTemplate.queryForMap(String.format(sql, m2, m1));
//        re2.put("monthly", -2);
//        results.add(re2);
        return results;
    }

}
